class Enum(object):
    def __init__(self, *options):
        self.options = options
    
    def __call__(self, string):
        for option in self.options:
            if option == string:
                return option
        raise Exception, "Given value \"" + string + "\" is not a valid enumeration " + \
            "value (" + ",".join(self.options) + ")"

    def __str__(self):
        return "Enum(" + ",".join(self.options) + ")"

class Bool(object):
    """
    Built-in bool type does not convert "False" properly.
    Thus, we have supplied our own.
    """
    def __call__(self, string):
        if string == "False":
            return False
        elif string == "True":
            return True
        else:
            raise Exception, "Bool expects \"True\" or \"False\" but was given " + \
                "\"" + string + "\""

    def __str__(self):
        return "bool"

class Parameterizable(object):
    # This must be overloaded in base classes
    # that want to define their own parameters
    parameter_definitions = {} 
    
    def __init__(self, *args, **kwargs):
        pass
    
    def configure_parameters(self, **parameters):
        return self.__class__.parse_parameters(parameters)

    @classmethod
    def get_plugin_name(cls):
        """
        Returns a string that uniquely identifies
        a subclass.
        """
        return cls.plugin_name

    @classmethod
    def parse_parameters(cls, user_parameters):
        """
        Parses the dictionary of user-provided parameters
        given in user_parameters according to what is
        defined by the dictionary returned by
        get_parameters.
        """
        params_def = cls.get_parameters()
        converted_params = {}

        # set defaults for optional parameters and checks for
        # required parameters
        for key, definition in params_def.iteritems():
            # required?
            required = definition[1]
            if required and key not in user_parameters:
                raise Exception, "Parameter \"" + key + "\" is " + \
                    "required but user only supplied parameters: " + \
                    ",".join(user_parameters.keys())
            elif not required:
                default = definition[3]
                converted_params[key] = default

        # parse given parameters
        for key, value_str in user_parameters.iteritems():
            # handle unknown params
            if key not in params_def:
                error = "Given parameter \"" + str(key) + "\" is unknown for " + \
                    "the plugin with the name \"" + str(cls.get_plugin_name()) + \
                    "\" with the parameters: " + ", ".join(params_def.keys())
                raise KeyError, error

            definition = params_def[key]
            param_type = definition[0]
            # convert param from string to type
            try:
                value = param_type(value_str)
                converted_params[key] = value
            except:
                raise Exception, "Could not convert given value \"" + value_str + \
                    "\" for parameter \"" + key + "\" of plugin with name \"" + \
                    cls.get_plugin_name + "\" to type \"" + str(param_type) + "\""
            
        return converted_params


    @classmethod
    def get_parameters(cls):
        """
        Returns a dictionary defining the keyword arguments
        that the constructor of the subclass takes.

        The keys of the dictionary are strings which identify
        the keywords; these will be used when passing the
        keyword argument into the constructor.

        The values are tuples of a type, a Boolean indicating
        whether the parameter is required or not, a string describing
        the parameter and possible values, and if the
        parameter is optional, a default value. (True is
        required, False is optional)

        E.g.,

        { "timestep" : (float, True, "Amount of time to simulate in each step of the integrator"),
          "enable_debugging" : (plugin_types.Bool, False, "Enable debugging output? True or False", True),
          "filename" : (str, True, "Filename to read (string)"),
          "option_a" : (str, False, "Option a (string)", "a"),
          "option_b" : (int, False, "Option b (int)", 1)}
        """
        return dict(cls.parameter_definitions)
